'use strict';

const ejs = require('ejs');
const loader = require('./sql_loader');

// 私有属性名：数据库连接
const _LOGGER = Symbol('MysqlClient#logger');

// 私有属性名：数据库连接
const _CONN = Symbol('MysqlClient#conn');

class MysqlClient {
  constructor(connection, logger) {
    this[_CONN] = connection;
    this[_LOGGER] = logger;
  }

  /**
   * 根据 SQL_ID 准备 SQL
   * @param {*} sqlid SQL ID
   * @param {*} data 参数
   */
  prepareSqlById(sqlid, data) {
    // 加载sql配置
    const cfg = loader.load(sqlid);
    if (!cfg) {
      throw new Error('Cannot find sql: ' + sqlid);
    }

    return this.prepareSql(cfg.sql, data);
  }

  /**
   * 准备SQL
   * @param {string} sql_tpl SQL模板
   * @param {Object} data 参数
   */
  prepareSql(sql_tpl, data) {
    // sql模板渲染
    let tpl = ejs.render(sql_tpl, data);
    tpl = tpl.replace(/\n{2,}/g, '\n');

    // 准备SQL
    const reg = /\$\{([^}]+)\}/g;
    const sql = tpl.replace(reg, '?');

    // 准备参数
    const param = [];
    let arr = null;
    while ((arr = reg.exec(tpl)) != null) {
      const key = arr[1];
      param.push(data[key]);
    }
    return { sql, param };
  }

  /**
   * 根据SQL ID执行SQL
   * @param {string} sqlid SQL ID
   * @param {Object} data 数据
   */
  execute(sqlid, data) {
    const statement = this.prepareSqlById(sqlid, data);
    this[_LOGGER].info(
      'execute sql %s\nparams: %s\n%s',
      sqlid,
      JSON.stringify(statement.param),
      this[_CONN].format(statement.sql, statement.param)
    );

    return new Promise((resolve, reject) => {
      this[_CONN].query(statement.sql, statement.param, (error, results) => {
        if (error) {
          reject(error);
        }
        resolve(results);
      });
    });
  }

  /**
   * 直接执行SQL语句
   * @param {string} sql SQL模板
   * @param {Object} data 数据
   */
  executeSql(sql, data) {
    const statement = this.prepareSql(sql, data);
    this[_LOGGER].info(
      'execute sql\nparams: %s\n%s',
      JSON.stringify(statement.param),
      this[_CONN].format(statement.sql, statement.param)
    );
    return new Promise((resolve, reject) => {
      this[_CONN].query(statement.sql, statement.param, (error, results) => {
        if (error) {
          reject(error);
        }
        resolve(results);
      });
    });
  }

  /**
   * 原始数组参数查询方式
   *
   * @param {string} sql 待执行SQL
   * @param {Array} param 参数，数组格式
   */
  query(sql, param) {
    this[_LOGGER].info(
      'execute sql\nparams: %s\n%s',
      JSON.stringify(param),
      this[_CONN].format(sql, param)
    );
    return new Promise((resolve, reject) => {
      this[_CONN].query(sql, param, (error, results) => {
        if (error) {
          reject(error);
        }
        resolve(results);
      });
    });
  }

  /**
   * 释放数据库
   */
  async release() {
    if (this[_CONN]) {
      await this[_CONN].release();
    }
    delete this[_CONN];
    delete this[_LOGGER];
  }

}

module.exports = MysqlClient;
